; ===================================
; MD3 Loader and Drawer
; 2013 Kirpi4
; ===================================

Enumeration 
	#MD3_IDENT 
	#MD3_VERSION
	#MD3_FLAGS = 3
	#MD3_NUM_FRAMES
	#MD3_NUM_TAGS
	#MD3_NUM_SURFACES
	#MD3_NUM_SKINS
	#MD3_OFF_FRAMES
	#MD3_OFF_TAGS
	#MD3_OFF_SURFACES
EndEnumeration

Enumeration 
	#MD3_SURF_IDENT 
	#MD3_SURF_FLAGS = 2
	#MD3_SURF_NUM_FRAMES
	#MD3_SURF_NUM_SHADERS
	#MD3_SURF_NUM_VERTS
	#MD3_SURF_NUM_TRIANGLES
	#MD3_SURF_OFF_TRIANGLES
	#MD3_SURF_OFF_SHADERS
	#MD3_SURF_OFF_COORDS
	#MD3_SURF_OFF_VERTS
	#MD3_SURF_EOF
EndEnumeration


; Vertex
Structure MD3Vert
	x.f
	y.f
	z.f
	n.l
EndStructure

; Frame
Structure MD3Frame
	min.vec3
	max.vec3
	orig.vec3
	rad.f
	name.s
EndStructure

; VertexFrame (one frame of anim)
Structure MD3AnimFrame
	Array verts.MD3Vert(0)
EndStructure

; Tag
Structure MD3Tag
	name.s
	orig.vec3
	orient.vec3[3]
EndStructure

; Triangle
Structure MD3Tri
	index.l[3]
EndStructure


; Shader (i.e. texture)
Structure MD3Shader
	name.s
	number.l
EndStructure

; Surface
Structure MD3Surf
	header.l[12]
	name.s ; Must be number 2 in header
	
	Array shaders.MD3Shader(0)
	Array tris.MD3Tri(0)
	Array coords.vec2(0)
	Array anims.MD3AnimFrame(0)
	
	Array arrTexCoords.vec2(0)
	Array arrIndices.l(0)
EndStructure

; MD3 Entity
Structure MD3Entity
	header.l[12]
	name.s ; Must be number 3 in header
	
	Array frame.MD3Frame(0)
	Array tag.MD3Tag(0)
	Array surf.MD3Surf(0)
EndStructure

Procedure.s MD3BufferString(f.l,len.l)
	tmp.s = ""
	For i=0 To len-1
		tmp + Chr(ReadByte(f))
	Next
	ProcedureReturn RTrim(tmp)
EndProcedure

Procedure.f MD3Interpolate(x1.f,x2.f,interp.f)
	dlt.f = x2-x1
	ProcedureReturn x1 + (dlt*interp)
EndProcedure

Procedure MD3ReadVec(*v.vec3,f.l)
	*v\x = ReadFloat(f)
	*v\y = ReadFloat(f)
	*v\z = ReadFloat(f)
EndProcedure

Procedure.l MD3NearPowerOf2(size.l)
	For i=0 To 31
		If size<Pow(2,i)
			ProcedureReturn Pow(2,i)
		EndIf
	Next
EndProcedure

Procedure LoadMD3(*m.md3entity,file.s)
	f = ReadFile(-1,file)
	
	; Header
	For i=0 To 11
		If i=2
			*m\name = MD3BufferString(f,64)
		Else
			*m\header[i] = ReadLong(f)
		EndIf
	Next
	
	; Frames
	FileSeek(f,*m\header[#MD3_OFF_FRAMES])
	Dim *m\frame(*m\header[#MD3_NUM_FRAMES])
	For i=0 To *m\header[#MD3_NUM_FRAMES]-1
		With *m\frame(i)
			MD3ReadVec(@\min,f)
			MD3ReadVec(@\max,f)
			MD3ReadVec(@\orig,f)
			\rad = ReadFloat(f)
			\name = MD3BufferString(f,16)
		EndWith
	Next
	
	; Tags
	FileSeek(f,*m\header[#MD3_OFF_TAGS])
	Dim *m\tag(*m\header[#MD3_NUM_TAGS])
	For i=0 To *m\header[#MD3_NUM_TAGS]-1
		With *m\tag(i)
			\name = MD3BufferString(f,64)
			MD3ReadVec(@\orig,f)
			For j=0 To 2
				MD3ReadVec(@\orient[j],f)
			Next
		EndWith
	Next
	
	; Surfaaaaaaaces
	FileSeek(f,*m\header[#MD3_OFF_SURFACES])
	Dim *m\surf.MD3Surf(*m\header[#MD3_NUM_SURFACES])
	For srf=0 To *m\header[#MD3_NUM_SURFACES]-1
		With *m\surf(srf)
			surfStart.l = Loc(f)
			; Header
			For i=0 To 11
				If i=1 
					\name = MD3BufferString(f,64)
				Else
					\header[i] = ReadLong(f)
				EndIf
			Next
			
			; Shaders
			FileSeek(f,surfStart+\header[#MD3_SURF_OFF_SHADERS])
			Dim *m\surf(srf)\shaders.MD3Shader(\header[#MD3_SURF_NUM_SHADERS])
			For i=0 To \header[#MD3_SURF_NUM_SHADERS]-1
				*m\surf(srf)\shaders(i)\name = GetFilePart(MD3BufferString(f,64))
				*m\surf(srf)\shaders(i)\number = ReadLong(f)
			Next
			
			; Triangles
			FileSeek(f,surfStart+\header[#MD3_SURF_OFF_TRIANGLES])
			Dim *m\surf(srf)\tris.MD3Tri(\header[#MD3_SURF_NUM_TRIANGLES])
			For i=0 To \header[#MD3_SURF_NUM_TRIANGLES]-1
				For j=0 To 2
					*m\surf(srf)\tris(i)\index[j] = ReadLong(f)
				Next
			Next
			
			; Coords
			FileSeek(f,surfStart+\header[#MD3_SURF_OFF_COORDS])
			Dim *m\surf(srf)\coords.vec2(\header[#MD3_SURF_NUM_VERTS])
			For i=0 To \header[#MD3_SURF_NUM_VERTS]-1
				*m\surf(srf)\coords(i)\x = ReadFloat(f)
				*m\surf(srf)\coords(i)\y = ReadFloat(f)
			Next
			
			; Verts
			FileSeek(f,surfStart+\header[#MD3_SURF_OFF_VERTS])
			Dim *m\surf(srf)\anims.MD3AnimFrame(*m\header[#MD3_NUM_FRAMES])
			For j=0 To *m\header[#MD3_NUM_FRAMES]-1
				Dim *m\surf(srf)\anims(j)\verts.MD3Vert(\header[#MD3_SURF_NUM_VERTS])
				For i=0 To \header[#MD3_SURF_NUM_VERTS]-1
						*m\surf(srf)\anims(j)\verts(i)\x = ReadWord(f)/64.0
						*m\surf(srf)\anims(j)\verts(i)\y = ReadWord(f)/64.0
						*m\surf(srf)\anims(j)\verts(i)\z = ReadWord(f)/64.0
						*m\surf(srf)\anims(j)\verts(i)\n = ReadWord(f)
				Next
			Next
			FileSeek(f,surfstart+\header[#MD3_SURF_EOF])
			
			; Preparing for array rendering
			Dim \arrIndices(\header[#MD3_SURF_NUM_TRIANGLES]*3)
			Dim \arrTexCoords(\header[#MD3_SURF_NUM_VERTS])
			For tr = 0 To \header[#MD3_SURF_NUM_TRIANGLES]-1
				For vr = 0 To 2
					\arrIndices(tr*3+vr) = \tris(tr)\index[vr]
				Next
			Next
			For vr = 0 To \header[#MD3_SURF_NUM_VERTS]-1
				\arrTexCoords(vr)\x = \coords(vr)\x
				\arrTexCoords(vr)\y = \coords(vr)\y
			Next
		EndWith
	Next

	CloseFile(f)
EndProcedure

Procedure.l CreateMeshFromMD3(*m.md3entity, frame1.l,frame2.l,trans.f)
	If frame1>=frame2
		Swap frame1, frame2
		trans = 1.0-trans
	EndIf
	
	Protected NOS.l = *m\header[#MD3_NUM_SURFACES]
	
	;Allocate new mesh
	Protected *newmesh.Mesh = AllocateMemory(SizeOf(Mesh))
	Dim *newmesh\SubMeshes(NOS-1)
	
	Protected Dim voidNormals.vec3(0), Dim voidTangents.vec3(0)
	
	For i=0 To NOS-1
	  With *m\surf(i)
			Protected Dim arrPositions.vec3(*m\surf(i)\header[#MD3_SURF_NUM_VERTS])
			
			;Fill vertex array
			For v = 0 To \header[#MD3_SURF_NUM_VERTS]-1
				arrPositions(v)\x = MD3Interpolate(\anims(frame1)\verts(v)\x,\anims(frame2)\verts(v)\x,trans)
				arrPositions(v)\y = MD3Interpolate(\anims(frame1)\verts(v)\y,\anims(frame2)\verts(v)\y,trans)
				arrPositions(v)\z = MD3Interpolate(\anims(frame1)\verts(v)\z,\anims(frame2)\verts(v)\z,trans)
			Next
			
			Protected *VAO.VAO = CreateVAO2(arrPositions(), voidNormals(), voidTangents(), \arrTexCoords(), \arrIndices(), #GL_DYNAMIC_DRAW)
			CopyStructure(*VAO, @*newmesh\SubMeshes(i)\VAO, VAO)
			FreeMemory(*VAO); Copy also stored in the mesh
			
			; Bound box
			*newmesh\BBoxMin\x = MD3Interpolate(*m\frame(frame1)\min\x,*m\frame(frame2)\min\x,trans)
			*newmesh\BBoxMin\y = MD3Interpolate(*m\frame(frame1)\min\y,*m\frame(frame2)\min\y,trans)
			*newmesh\BBoxMin\z = MD3Interpolate(*m\frame(frame1)\min\z,*m\frame(frame2)\min\z,trans)
			*newmesh\BBoxMax\x = MD3Interpolate(*m\frame(frame1)\max\x,*m\frame(frame2)\max\x,trans)
			*newmesh\BBoxMax\y = MD3Interpolate(*m\frame(frame1)\max\y,*m\frame(frame2)\max\y,trans)
			*newmesh\BBoxMax\z = MD3Interpolate(*m\frame(frame1)\max\z,*m\frame(frame2)\max\z,trans)
			
			;other info
			*newmesh\SubMeshes(i)\Mat\TextureName = \shaders(0)\name
			*newmesh\SubMeshes(i)\StartPosition = 0
			*newmesh\SubMeshes(i)\NumOfIndices = \header[#MD3_SURF_NUM_TRIANGLES]*3
	  EndWith
	Next
	
	ProcedureReturn *newmesh
EndProcedure

Procedure.l CreateAnimatedMeshFromMD3(*m.md3entity)	
	Protected numOfSurfaces.l = *m\header[#MD3_NUM_SURFACES]
	Protected numOfFrames.l = *m\header[#MD3_NUM_FRAMES]
	
	Protected Dim voidNormals.vec3(0), Dim voidTangents.vec3(0)
	
	;Allocate new mesh
	Protected *newmesh.AnimatedMesh = AllocateMemory(SizeOf(AnimatedMesh))
	Dim *newmesh\SubMeshes(numOfSurfaces-1)
	
	
	For i=0 To numOfSurfaces-1
	  With *m\surf(i)
			Protected Dim arrPositions.vec3(\header[#MD3_SURF_NUM_VERTS] * numOfFrames)
			
			;Fill vertex array
			Protected pos.l = 0
			For frame = 0 To numOfFrames-1
				For v = 0 To \header[#MD3_SURF_NUM_VERTS]-1
					arrPositions(pos)\x = \anims(frame)\verts(v)\x
					arrPositions(pos)\y = \anims(frame)\verts(v)\y
					arrPositions(pos)\z = \anims(frame)\verts(v)\z
					
					pos = pos + 1
				Next
			Next
			
			Protected *VAO.VAO = CreateVAO2(arrPositions(), voidNormals(), voidTangents(), \arrTexCoords(), \arrIndices(), #GL_DYNAMIC_DRAW)
			CopyStructure(*VAO, @*newmesh\SubMeshes(i)\VAO, VAO)
			FreeMemory(*VAO); Copy also stored in the mesh
			
			;TODO calculate bounding box
			CopyStructure(@*m\frame(0)\min, @*newmesh\BBoxMin, vec3)
			CopyStructure(@*m\frame(0)\max, @*newmesh\BBoxMax, vec3)
			
			*newmesh\SubMeshes(i)\Mat\TextureName = \shaders(0)\name
			*newmesh\SubMeshes(i)\StartPosition = 0
			*newmesh\SubMeshes(i)\NumOfIndices = \header[#MD3_SURF_NUM_TRIANGLES]*3
			*newmesh\SubMeshes(i)\NumOfVertices = \header[#MD3_SURF_NUM_VERTS]
			
			*newmesh\NumOfFrames = numOfFrames
	  EndWith
	Next
	
	ProcedureReturn *newmesh
EndProcedure
; IDE Options = PureBasic 5.11 (Windows - x86)
; CursorPosition = 282
; FirstLine = 266
; Folding = --
; EnableXP
; EnableCompileCount = 0
; EnableBuildCount = 0
; EnableExeConstant